from openai import OpenAI
import re
from structs import Plan, Project
import codetest
import config
pattern = re.compile(r'box_(\d)<(.*?)><(.*?)>')

def get_chat_response(
    system_prompt: str,
    user_prompt: str,
    model: str ,
    temperature: float = 0.1,
    max_tokens: int = 500,
) -> str:
    """
    通过系统提示和用户提示生成对话响应
    
    参数:
    system_prompt (str): 系统角色定义提示
    user_prompt (str): 用户输入的提问内容
    model (str): 模型名称（如"gpt-3.5-turbo"）
    temperature (float): 输出随机性（0-1）
    max_tokens (int): 最大生成标记数
    api_key (str): OpenAI API密钥
    base_url (str): API基础地址（可选自定义）
    
    返回:
    str: AI生成的文本内容
    """
    try: # 添加 try...except 块捕获潜在错误
        # 初始化客户端
        client = OpenAI(
            api_key=config.api_key,
            base_url=config.base_url,
            timeout=60
        )
        
        # 构建消息格式
        messages = [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt}
        ]
        
        # 发送请求
        response = client.chat.completions.create(
            model=model,
            messages=messages,
            temperature=temperature,
            max_tokens=max_tokens
        )
        
        # --- 添加健壮性检查 ---
        if response and response.choices and len(response.choices) > 0:
            choice = response.choices[0]
            if choice.message and choice.message.content:
                return choice.message.content.strip()
            else:
                print("错误：API 响应中缺少 message 或 content。")
                return "抱歉，无法从 API 获取有效的响应内容。" # 返回错误信息
        else:
            print("错误：API 响应无效或 choices 列表为空。")
            return "抱歉，未能从 API 获取响应。" # 返回错误信息
        # --- 检查结束 ---

    except Exception as e: # 捕获 API 调用或其他意外错误
        print(f"调用 get_chat_response 时发生错误: {e}")
        # 可以根据需要记录更详细的错误信息
        # import traceback
        # traceback.print_exc()
        return f"抱歉，处理请求时发生错误: {e}" # 返回包含错误的字符串

def agent_planer(
    lang: str,
    方向: str
) -> Plan:
    system_prompt = "你是一个计划师，你需要依据用户选择的编程语言和学习方向，制作计划，计划分为十步，使用box_i<计划内容><介绍>的格式输出。例如：box_0<第0步内容><第零步介绍>...box_9<第9步内容><第九步介绍>,不要输出具体内容，只输出标题"
    user_prompt = f"编程语言：{lang}，学习方向：{方向}"
    
    # 调用聊天接口获取响应
    response = get_chat_response(
        system_prompt=system_prompt,
        user_prompt=user_prompt,
        max_tokens=3000,
        model=config.planer_model
    )

    
    # 使用正则表达式匹配响应内容
    matches = pattern.findall(response) 
    
    # 解析响应内容为两个数组
    plans = ["" for _ in range(10)]
    intro = ["" for _ in range(10)]
    for index_str, 计划, 介绍 in matches:
        index = int(index_str)
        plans[index] = 计划.strip()
        intro[index] = 介绍.strip()
    
    return Plan(lang, 方向, plans, intro,0)

def agent_project_maker(
    plan: Plan,
    user_level: int,
) -> Project:
    # 获取当前学习阶段的计划内容和描述
    current_plan = plan.plans[user_level]
    current_intro = plan.intro[user_level]
    # 构建系统提示词
    system_prompt = """
    
你是一个编程学习项目生成专家，根据用户提供的以下信息生成编程练习项目：
- 编程语言
- 学习方向/技术领域
- 当前学习重点
- 项目功能描述

生成要求：
1. 创建一个单文件实现的小型练习项目，请注意这个项目的输入与输出均进行一次
2. 在代码中用注释标记练习点，格式为：
    练习点-[序号]: [描述练习内容]
    请**留空**练习点所涉及到的代码,需要用户自行完成。
    请**留空**练习点所涉及到的代码,需要用户自行完成。
    请**留空**练习点所涉及到的代码,需要用户自行完成。
3. 生成配套说明文档，包含：
   - 项目功能简介
   - 练习点列表及位置说明
   - 预期实现效果
   - 测试用例3个(输入：[输入]，输出：[输出])
4.代码中不要包含任何示例操作，如果原先有，去除之。
5.代码中在输入前不要输出任何提示内容
提示1：用户是初学者，请勿使用高级语法和复杂功能，尽量简单易学。
提示2：在输出markdown文档内容之前，请仅仅输出代码，使用plain text格式

输出格式：
[代码内容]
---
[Markdown文档内容]
    """
    
    # 构建用户输入提示
    user_prompt = f"""
    语言：{plan.lang}
    学习方向：{plan.方向}
    当前学习计划：{current_plan}
    计划描述：{current_intro}
    请生成符合要求的项目代码和文档：
    """
    
    # 获取AI生成的响应
    response = get_chat_response(
        system_prompt=system_prompt,
        user_prompt=user_prompt,
        max_tokens=3000,
        temperature=0.5, #防止多次请求完全相同
        model=config.project_maker_model
    )
    
    # 分割响应内容
    parts = response.split('---', 1)
    code_part = parts[0].strip()
    md_part = parts[1].strip()
     # 清理代码部分，删除包含 ``` 的行
    cleaned_code = "\n".join([line for line in code_part.splitlines() if "```" not in line])
    return agent_knowledge_explainer(Project(cleaned_code, md_part))

def agent_testcase_generator(
    project: Project
) -> list[tuple[str, str]]:
    """
    生成测试用例列表
    
    参数:
    project (Project): 包含代码和文档的项目对象
    
    返回:
    list[tuple[str, str]]: 测试样例列表，每个样例为(input, expected_output)元组
    """
    system_prompt = """
    你是一个编程练习项目测试用例生成专家，根据用户提供的以下信息生成编程练习项目测试用例：
    - 项目代码
    - 项目描述

    生成要求：
    - 生成5个测试用例，每个用例包含一组输入和一组输出
    - 请注意testcase格式为用户在终端所见的格式，不要使用json,markdown或者其他格式,除非程序真的应该以特殊格式输出
    - testcase格式为：
    test_1<input:你的输入,output:你的输出>
    test_2<input:你的输入,output:你的输出>
    ...
    test_5<input:你的输入,output:你的输出>
    """ 
    user_prompt = f"""
    项目代码：
    {project.code}
    项目描述：
    {project.md}
    请生成符合要求的测试用例：
    """
    response = get_chat_response(
        system_prompt=system_prompt,
        user_prompt=user_prompt,
        max_tokens=3000,
        model=config.testcase_generator_model
    )
    
    # 使用正则表达式提取测试用例
    test_case_pattern = re.compile(r'test_\d+<input:(.*?),output:(.*?)>')
    matches = test_case_pattern.findall(response)
    #如果有\n,替换为空格
    matches = [(input_str.replace('\n', ' '), output_str.replace('\n', ' ')) for input_str, output_str in matches]
    # 返回(input, output)元组列表
    return [(input_str.strip(), output_str.strip()) for input_str, output_str in matches]

def agent_code_completer(
    project: Project
) -> str:
    """
    根据项目代码和描述，生成完整的参考答案代码。

    参数:
    project (Project): 包含初始代码和文档的项目对象

    返回:
    str: AI生成的完整参考代码
    """
 
    system_prompt = """
    你是一个资深软件工程师，擅长根据项目描述和不完整的代码框架，补全代码并提供最终的参考答案。
    用户会提供包含练习点的初始代码和项目描述文档。
    你的任务是：
    1. 理解项目的功能需求和练习点。
    2. 补全代码，确保代码能够正确运行并实现预期功能。
    3. 保留代码中原有的注释，特别是练习点标记。
    4. 只输出最终的完整代码，不要包含任何额外的解释、说明或 ``` 标记。
    5. 不要包含任何示例操作，如果原先有，去除之。
    """

    # 2. 构建 user_prompt
    # 注意：在 user_prompt 中明确告知模型代码语言，有助于生成更准确的代码
    # 尝试从 project.md 或 project.code 中提取语言信息，如果 project 对象没有直接提供的话
    # 这里暂时假设可以通过 project.lang 获取，如果不行，需要调整
    # 或者让模型自行判断，但明确指定更好
    # 暂时移除 ```{project.lang} 标记，因为 project 对象目前没有 lang 属性
    user_prompt = f"""
    请根据以下项目描述和初始代码，补全代码并提供最终的参考答案。

    项目描述:
    {project.md}

    初始代码:
    ```
    {project.code}
    ```

    请只输出补全后的完整代码。
    """

    # 3. 调用 get_chat_response
    # 注意：需要确保 config.py 中定义了 code_completer_model
    completed_code = get_chat_response(
        system_prompt=system_prompt,
        user_prompt=user_prompt,
        model=config.code_completer_model, 
        temperature=0.2, 
        max_tokens=3000 
    )

    # 4. 清理输出 (类似 project_maker)
    # 移除可能存在的 markdown 代码块标记
    cleaned_code = "\n".join([line for line in completed_code.splitlines() if "```" not in line])

    # 5. 返回结果
    return cleaned_code


def agent_knowledge_explainer(
    project: Project
) -> Project:

    system_prompt = """
    你是一位优秀的编程导师，擅长用通俗易懂的语言向编程小白解释项目背后涉及的核心知识点和原理。
    你的任务是根据用户提供的项目描述文档，生成一段清晰、简洁的知识点原理讲解。
    讲解内容应聚焦于项目最核心的概念，帮助初学者理解项目的目标和实现思路。
    **使用markdown格式**，请直接输出讲解内容，不需要任何前缀或标题，专注于解释本身。
    
    """

    # 2. 构建 User Prompt
    user_prompt = f"""
    请根据以下项目描述文档，生成一段知识点原理讲解，帮助编程小白理解其核心概念。

    项目描述文档:
    ---
    {project.md}
    ---
    项目代码：
    ---
    {project.code}
    ---
    请生成讲解内容：
    """

    # 3. 调用 get_chat_response 获取讲解内容
    # 使用 config.project_maker_model 如用户要求
    response = get_chat_response(
        system_prompt=system_prompt,
        user_prompt=user_prompt,
        model=config.project_maker_model,
        temperature=0.3,
        max_tokens=1000
    )
    if not isinstance(response, str):
        return project
    explanation = response

    # 4. 组合新的 Markdown 内容
    # 将讲解加在原文档前面，并用分隔符和标题隔开
    new_md_part = f"""## 知识点讲解

{explanation.strip()}

---

{project.md}"""

    # 5. 创建并返回新的 Project 对象
    # 注意：Project 结构是 (code, md)，所以我们用 project.code 和 new_md_part
    updated_project = Project(code=project.code, md=new_md_part)
    return updated_project

#评价者
def agent_evaluator(
    project: Project,
    code: str,
    result: list # <-- 修正类型提示为 list
) -> str:
    """
    根据代码和测试用例，评价代码的实现质量。如果有错误则指出
    注意：参数 'result' 期望是一个测试结果字典的列表，而不是包含 'all_passed' 的外层字典。
    """
    # 1. 构建 System Prompt
    system_prompt = """
    你是一位经验丰富的编程导师和代码评审员。你的任务是根据提供的项目描述、用户提交的代码以及代码的测试结果，对用户的代码进行评估。

    评估要求：
    1. 分析用户代码的逻辑是否符合项目描述的要求。
    2. 检查代码的测试结果。用户提供的测试结果列表如下。
    3. 如果测试结果显示存在错误（编译错误、运行时错误或逻辑错误导致测试失败），请明确指出错误的原因，并结合具体的测试用例进行说明。
    4. 如果代码通过了所有测试，可以简要表扬，并可以根据代码质量（例如，可读性、效率、是否遵循最佳实践等，但不必过于苛刻，考虑用户是初学者）提出一些改进建议。
    5. 评价语言应客观、清晰、具有建设性。
    6. 直接输出评价内容，不需要任何前缀或标题。
    """

    # 2. 构建 User Prompt，格式化测试结果列表
    test_details_str = ""
    all_passed_flag = True # 假设全部通过，如果遇到失败则设为 False
    # 检查 result 是否为列表且非空
    if isinstance(result, list) and result: # 直接检查 result 是否为列表
        for i, res in enumerate(result): # 直接迭代列表 result
            # 确保 res 也是字典
            if isinstance(res, dict):
                passed = res.get('passed', False) # 获取通过状态
                if not passed:
                    all_passed_flag = False # 发现未通过的测试用例

                status = "通过" if passed else "失败"
                input_val = res.get('input', '未知')
                expected_output = res.get('expected', '未知')
                actual_output = res.get('actual', '无输出').strip()
                error = res.get('error', '无').strip()
                test_details_str += (
                    f"  测试 {i+1}:\n"
                    f"    状态: {status}\n"
                    f"    输入:\n```\n{input_val}\n```\n"
                    f"    预期输出:\n```\n{expected_output}\n```\n"
                    f"    实际输出:\n```\n{actual_output}\n```\n"
                    f"    错误信息: {error}\n\n"
                )
            else:
                 test_details_str += f"  测试 {i+1}: 结果格式错误 (非字典)\n\n"
                 all_passed_flag = False # 格式错误也视为未通过
    else:
        test_details_str = "未能获取详细测试结果或结果列表为空/格式错误。\n"
        all_passed_flag = False # 没有结果视为未通过

    # 基于 all_passed_flag 设置 execution_success_str
    execution_success_str = '是' if all_passed_flag else '否'
    # 错误信息在每个测试用例里，这里不展示顶层错误
    execution_error_str = '（具体错误请看下方详细结果）' if not all_passed_flag else '无'

    user_prompt = f"""
项目描述:
{project.md}

用户提交的代码:
```
{code}
```

测试总结:
所有测试用例是否都通过: {execution_success_str}
执行时错误总结: {execution_error_str}

详细测试用例结果:
{test_details_str}
请根据以上信息，对用户的代码进行评价：
"""

    # 3. 调用 get_chat_response 获取评价
    evaluation = get_chat_response(
        system_prompt=system_prompt,
        user_prompt=user_prompt,
        model=config.evaluator_model,
        temperature=0.3,
        max_tokens=2000
    )

    # 4. 返回评价结果
    return evaluation.strip()
